/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID coidgen tool.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Robo Strycek
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */



#ifndef __COIDGEN_H
#define __COIDGEN_H



#include "coid/comm/comm.h"
#include "coid/comm/dynarray.h"
#include "coid/comm/str.h"


#include "coid/svc/coidsvc.h"



#include "stdstrings.h"
#include "str.h"



namespace coid {



enum OUTPUT_TYPE
{
	T_client_header, T_client_cpp, T_client_cpp_inline_fnc, T_client_class_in_cpp, T_function_table, T_client_function_hook,
	T_function_hook, T_dispatch_cpp
};


////////////////////////////////////////////////////////////////////////////////
struct H_FILE;

struct BLOCK
{
	charstr text;
	uint offset;
	ushort index_before_class;
	ushort flags;

	enum {fskip=0, fclient_only=1, fadd_to_client=2, fclient=3, fnone=0x8000};

	BLOCK() : offset(0), index_before_class(0xFFFF), flags(fnone) {}

	void stream_to( charstr& out, OUTPUT_TYPE t ) const;
};


////////////////////////////////////////////////////////////////////////////////
/**
	main class that holds info about a class / struct
*/

struct CLASS
{
	H_FILE * file;
	CLASS * parent;		/// parent class (if any)

	/* function info
	*/
	struct FUNCTION
	{
		CLASS * parent;
		const charstr & get_class_name() {return parent->name;}

		// arguments are stored here:
		struct ARG
		{
			FUNCTION * parent;

			uint arg_count;			/// index to argument, that stores number of elements this argument points to
									/// (only for pointers)
			ARG * arg_count_eval;	/// argument, that stores evaluated number of elements
			ARG * type_remap;

			charstr type;
			charstr name;
			charstr count_string;	/// string saying how many items are there in an array (ptr only)
			charstr def_value;
			ushort asterix_count;

			int flags;
			enum {
				fin=1, fout=2, finout=4, ffetch=8, fconst=0x10, fnolog=0x20, faccount=0x40, freturn=0x80,
				fvoid=0x100, fstring=0x200,
				fptr=0x400, fref=0x800, fptr_count=0x1000, fcount=0x2000,
				fnet_ptr=0x4000,
				fopen_service=0x8000,
				fmalloc=0x10000,		/// default allocator is new; strings are always allocated by new
				fmapped=0x20000,		/// 'type_remap' is mapped argument (we don't generate handling for direct calls)

				f_typeremap_inherit=(fin | fout | finout | fmalloc),
				f_major_change_mask=(0xFFFF & ~(fconst | fnolog)),		/// flags incrementing major version
				f_minor_change_mask=0
			};



			ARG() : parent(NULL), arg_count(UMAX32), arg_count_eval(NULL), type_remap(NULL), asterix_count(0), flags(0)
			{}

			~ARG() {
				if( arg_count_eval ) delete arg_count_eval;
				if( type_remap ) delete type_remap;
			}

			void get_global_name ( charstr & out ) {out = parent->get_class_name(); out += "::"; out += name;}
			char * parse( char * ptr, bool is_return_type=false );
			void handle_default_flags( uint mask );
			int compare( const ARG & old ) const;
			bool is_array() const;
			bool is_localarray() const;
			bool is_remapped() const {return type_remap && ! (flags & fmapped);}
			bool is_mapped() const {return type_remap && (flags & fmapped);}

			bool stream_array( charstr & out ) const;

			enum ARG_DISPATCH_TYPE {
				adt_name, adt_count_name, adt_argument, adt_stream_in, adt_stream_out, adt_dispatch_fcall, adt_delete
			};
			void append_dispatch_arg_name( charstr & tmp, ARG_DISPATCH_TYPE type ) const;
			void append_client_arg_name( charstr & out, ARG_DISPATCH_TYPE type ) const;
			void stream_to( charstr & out, OUTPUT_TYPE t ) const;

			ARG * create_arg_count_eval() const {
				ARG * a_new = new ARG;
				a_new->name = name; a_new->name += STR_coid_count_append;
				a_new->type = "uint"; a_new->flags = ARG::fin;
				a_new->parent = NULL;
				return a_new;
			}

			ARG & operator = ( const ARG & x );
			friend binstream & operator << ( binstream & out, const ARG & x );
			friend binstream & operator >> ( binstream & in, ARG & x );
		};


		ARG		ret;
		charstr name;
		uint id;
		charstr main_comment;		/// comment above function
		charstr open_service;		/// name of service this function should open (only 1 per method allowed)
		dynarray<ARG> args;

		int handle_client_calls;		/// 1 == remmaped argument present, 2 == fetch arg present
		int flags;
		enum {
			fconst=1,
			fdirect=ServiceDescriptor::MethodDesc::fACCESS_MODE_DIRECT,	//0x10000
			finterprocess=ServiceDescriptor::MethodDesc::fACCESS_MODE_INTERPROCESS,	//0x20000
			fremote=ServiceDescriptor::MethodDesc::fACCESS_MODE_REMOTE,	//0x40000
			fcomm_mask=(fdirect | finterprocess | fremote),
			fnolog=0x100,
            fmeta=0x200,
            fnolock=0x400,
			fdispatch_empty_body=0x1000,
			fkeep_as_is=0x2000, fnot_present_in_service=0x4000,
			fspecial_fnc=0x800000,
			fspace_after_name=0x10000000, fspace_in_brackets=0x20000000,
			faccept_connect_within=0x80000000,
			f_visible_flags=(fconst | fcomm_mask | fnolog),		/// flags that will be written to dispatch or client source files
			f_major_change_mask=(0xFF | fcomm_mask) & ~fconst,		/// bit flags incrementing major version
			f_minor_change_mask=0,
		};


		FUNCTION() : parent(NULL), handle_client_calls(0), flags(0) {}
		void get_id_name( charstr & id_name ) const;
		void append_full_name( charstr & full_name ) const;
		char * parse( const char * base, char * ptr );
		bool none_arguments() const;
		bool void_return_type() const;
		bool opcd_return_type() const;

		const ARG * get_account_arg() const;
		int compare( const FUNCTION & old ) const;
		FUNCTION & operator = ( const FUNCTION & x );
		void client_streaming_to_stream( charstr & out, bool cci=true );
		void client_streaming_from_stream( charstr & out, bool cci=true );
		void stream_to( charstr & out, OUTPUT_TYPE t ) const;
		void stream_body_to( charstr & out, OUTPUT_TYPE t ) const;
		friend binstream & operator << ( binstream & out, const FUNCTION & x );
		friend binstream & operator >> ( binstream & in, FUNCTION & x );
	};


	charstr main_comment;		/// comment in front of class declaration
	charstr name, short_name;
	charstr full_name;
	charstr name_left, name_right;
    charstr name_space;
	charstr options, dependencies;
	uint acceptor_port;

	dynarray<BLOCK>		blocks;

	uints max_fnc_index;
	uints fnc_index;
	uints class_text_offset, class_text_offset_end;

	dynarray<FUNCTION *> fnc_export;

	enum {
		accept_connect=0, accept_connect_shared, accept_connect_auto,
		accept_net_connections,
		accept_destroy, accept_startup_params,
		accept_command, accept_spawn, accept_echo,
		SPEC_FNC_COUNT
	};
	FUNCTION * fnc_special[SPEC_FNC_COUNT];
	FUNCTION fnc_disconnect, fnc_dispatch;


	int flags;
	enum {
		fautonomous=ServiceDescriptor::fAUTONOMOUS,	//1
		fbindable=ServiceDescriptor::fBINDABLE,	//2
		//fshared=ServiceDescriptor::fSHARED,	//4
		fhas_spawn_point=ServiceDescriptor::fHAS_SPAWN_POINT,	//8
		fautorun=ServiceDescriptor::fAUTORUN,	//0x10
		facceptor_bound_session=ServiceDescriptor::fACCEPTOR_BOUND_SESSION,	//0x20
//		f_service_flags_mask=(fautonomous | fbindable | fshared | fhas_spawn_point | fautorun | facceptor_bound_session),
		fallowdirect=ServiceDescriptor::fALLOW_DIRECT_ACCESS,	//0x10000
		fallowinterprocess=ServiceDescriptor::fALLOW_INTERPROCESS_ACCESS,	//0x20000
		fallowremote=ServiceDescriptor::fALLOW_REMOTE_ACCESS,	//0x40000
		fallow_access_mask=(fallowdirect | fallowinterprocess | fallowremote),
		fnolog=0x100000, fnolog_syscalls=0x200000,
		fclient=0x400000, fstruct=0x800000, foption_read_info=0x1000000,
		f_major_change_mask=(0xFFFF | fallow_access_mask) & ~facceptor_bound_session,	/// bit flags incrementing major version
		f_minor_change_mask=0
	};



	CLASS() : file(NULL), parent(NULL), acceptor_port(UMAX32), max_fnc_index(0), fnc_index(0), flags(fautonomous)
	{
		memset( fnc_special, 0, SPEC_FNC_COUNT * sizeof(FUNCTION *) );
        name_space = "coid";
	}

	~CLASS() {
		uint i;
		for( i=0; i<SPEC_FNC_COUNT; i++ )
			if( fnc_special[i] ) delete fnc_special[i];
		for( i=0; i<fnc_export.size(); i++ )
			if( fnc_export[i] ) delete fnc_export[i];
	}

    void set_namespace( const char* ns )
    {
        ns += strspn(ns," \t\n\r");
        uints nsn = strcspn( ns, " \t;{\n\r");
        if( ns[nsn] != '\n' && ns[nsn] != '\r' )
            name_space.set_from( ns, nsn );
    }

	int parse( const char* base, char * p_start, char * p_name, char * p_cls_start, char * p_cls_end );
	bool check_functions();
	ints fill_comment( charstr& comment, uints offset, bool use_parent=false );
	int compare( const CLASS& old );
	void sort_functions_by( int what );		/// 0==fnc name, 1==id
	void write_init();

	uint get_function_index( const FUNCTION * f ) const {
		const FUNCTION * const * fe = fnc_export.ptr();
		for( uint i=0; i<fnc_export.size(); i++, fe++ )
			if( f == *fe ) return i;
		return UMAX32;
	}

	void stream_to( charstr & out, OUTPUT_TYPE t ) const;
	friend binstream & operator << ( binstream & out, const CLASS & x );
	friend binstream & operator >> ( binstream & in, CLASS & x );



	struct FNC_SORT_BY_NAME {
		bool operator () ( const FUNCTION * f1, const FUNCTION * f2 ) const {
            return f1->name < f2->name;
			//return strcmp( f1->name, f2->name ) < 0;
		}
	};
	struct FNC_SORT_BY_ID {
		bool operator () ( const FUNCTION * f1, const FUNCTION * f2 ) const {
			return f1->id < f2->id;
		}
	};
};






typedef CLASS::FUNCTION		FUNCTION;
typedef FUNCTION::ARG		ARG;


////////////////////////////////////////////////////////////////////////////////

struct HEADER
{
	charstr author;
	version ver;
	charstr date;
	charstr description;


	bool parse( const char * from );
	friend charstr & operator << ( charstr& out, const HEADER & x );
};


////////////////////////////////////////////////////////////////////////////////
struct H_FILE
{
	HEADER header;
	charstr release_version;		/// release version (if present in service.h)
	uint ver_next_major;

	dynarray<charstr>	comments;
	dynarray<BLOCK>		blocks;
	dynarray<CLASS *>	cls;

	dynarray<char *> templ_parsed;		/// for temporary usage (when writing output)
	const char * source_header_name;
	const char * client_header_name;
	char * class_members;
	char * header_info;


	int flags;
//	enum {ffillcopyright=1};

	H_FILE() : ver_next_major(1), source_header_name(NULL), client_header_name(NULL), class_members(NULL),
				header_info(NULL), flags(0)
	{
		header.ver.clear();
	}

	~H_FILE() {
		for( uint i=0; i<cls.size(); i++ )
			if( cls[i] ) delete cls[i];
		cls.discard();
	}

	void add_block( CLASS * cls_parent, const char * base, char * from, char *& to, ushort flags );
	int parse( CLASS * cls_parent, const char * base, char * from, char * to );
	int compare( const H_FILE & old ) const;
	enum {				/// 0xF > number <= 0xFFFF ==> minor version should by changed, number > 0xFFFF ==> major ver.
		diff_class_added=0x10000,					/// added / renamed class
		diff_class_deleted=0x20000,					///
		diff_class_changed_major=0x40000,			/// major flags changed
		diff_class_changed_minor=0x10,				/// minor flags changed
		diff_function_added=0x100,					///
//		diff_function_deleted=0x200,				///
		diff_function_deleted=0x80000,				///
		diff_function_changed=0x400000,				///
		diff_arg_added=0x1000000,
		diff_arg_deleted=0x2000000,
		diff_arg_changed_major=0x4000000,			/// major flags difference
		diff_arg_changed_minor=0x1000,				/// minor flags difference
		diff_arg_changed=0x8000000,					/// args do not match (strcmp() call)
	};

	int write_dispatch_cpp( char * templ, charstr & out, uint class_index );
	int write_client_cpp( char * templ, charstr & out, uint class_index );
    
    void write_finfo( charstr& out, charstr& method_desc, const CLASS::FUNCTION* fnc );
	void replace_template( bool dispatch_or_client, const char * templ, charstr & out, const CLASS * c, uint fnc_index=UMAX32 ) const;
	void replace_disp_template( uint templ_index, charstr & out, const CLASS * c, uint fnc_index ) const;
	void replace_client_template( uint templ_index, charstr & out, const CLASS * c, uint fnc_index ) const;
	char * add_comments( CLASS * cls_parent, const char * base, char *& p_block_start, char * ptr, char * to, char * first_class );
	bool contains_coid_include( const char * ptr );

	uint find_class( const CLASS * obj ) const {
		for( uint i=0; i<cls.size(); i++ )
			if( cls[i] == obj ) return i;
		return UMAX32;
	}

	static void remove_const( const charstr & name, charstr & out );

	void stream_to( charstr& out, OUTPUT_TYPE t ) const;
	friend binstream & operator << ( binstream & out, const H_FILE & x );
	friend binstream & operator >> ( binstream & in, H_FILE & x );
};



} //namespace coid










#endif	// ! __COIDGEN_H
